#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

#include "widget.h"
#include "callback.h"

// (private) size of the hash table
static size_t cbsize=0;
// (private) hash table contains hwnd - widget binding
static struct callback_node **cbmap=NULL;

void callback_alloc(size_t table_size)
{
    size_t i;
    // allocate memory for the hash table
    cbsize = table_size;
    assert(cbsize > 0);
    cbmap = malloc(sizeof(struct callback_node *) * cbsize);
    assert(cbmap != NULL);
    // create dummy nodes
    for(i=0; i<cbsize; i++)
    {
        struct callback_node *dummy = malloc(sizeof(struct callback_node));
        assert(dummy != NULL);
        dummy->hwnd = 0;
        dummy->widget = NULL;
        dummy->next = dummy; // point at it self
        cbmap[i] = dummy;
    }
}

void callback_bind(HWND hwnd, widget_t *widget)
{
    size_t hash = (uint32_t)hwnd % cbsize;
    // assert that the hwnd is not already binded
    struct callback_node *itr = cbmap[hash];
    while(itr->next != itr) {
        assert(itr->hwnd != hwnd);
        itr = itr->next;
    }
    // create a new node and add it the table
    struct callback_node *node = malloc(sizeof(struct callback_node));
    node->hwnd = hwnd;
    node->widget = widget;
    node->next = cbmap[hash];
    cbmap[hash]= node;
}

struct callback_node *callback_find_node(HWND hwnd)
{
    const size_t hash = (uint32_t)hwnd % cbsize;
    // search for the hwnd in the hash table
    struct callback_node *itr = cbmap[hash];
    while(itr != itr->next) {
        if(hwnd == itr->hwnd) {
            return itr;
        }
        itr = itr->next;
    }
    // should never go to here!
    fprintf(stderr, "callback_find_node error: hwnd not binded!\n");
    exit(-1);
}

widget_t *callback_find_widget(HWND hwnd)
{
    const size_t hash = (uint32_t)hwnd % cbsize;
    // search for the hwnd in the hash table
    struct callback_node *itr = cbmap[hash];
    while(itr != itr->next) {
        if(hwnd == itr->hwnd) {
            return itr->widget;
        }
        itr = itr->next;
    }
    // should never go to here!
    fprintf(stderr, "callback_find_widget error, hwnd no binded!\n");
    exit(-1);
}

void callback_free()
{
    size_t i;

    for(i=0; i<cbsize; i++)
    {
        struct callback_node *itr = cbmap[i];
        while(itr != itr->next) {
            struct callback_node *tmp = itr;
            // let the widget free it self
            assert(tmp->widget != NULL);
            tmp->widget->free(tmp->widget);
            // free the callback node
            free(tmp);
            itr = itr->next;
        }
        // remove the dummy node
        free(itr);
    }

    // free the hash table
    free(cbmap);
}

#ifdef  __UNIT_TEST_CALLBACK__

#undef main

int main(int argc, char *argv[])
{
    widget_t *w;
    callback_alloc(1);

    callback_bind((HWND)397347120,widget_create(NULL, -1,-1,100,100));
    callback_bind((HWND)200,widget_create(NULL,10,10,300,300));

    w = callback_find_widget((HWND)397347120);

    printf("%d\n", (int)w);

    callback_free();
    return 0;
}

#endif

